#include "head.h"
#include "cc9d_support.h"
#include "Battery.h"
#include "adc.h"
#include "stm32f10x.h"
#define SLB12498

#define LOCAL_PORT Battery_PORT

#ifdef SLB12491
#define _TWO_MOSFET_

#define EN_GPIOx GPIOB
#define EN_GPIO_Pin_n GPIO_Pin_0

#define ADC_CHANNEL ADC_Channel_9
#define ADC_GPIOx GPIOB
#define ADC_GPIO_Pin_n GPIO_Pin_1
#endif

#ifdef SLB12498

#define EN_GPIOx GPIOB
#define EN_GPIO_Pin_n GPIO_Pin_3

#define ADC_CHANNEL ADC_Channel_8
#define ADC_GPIOx GPIOB
#define ADC_GPIO_Pin_n GPIO_Pin_0

#endif



#ifndef printd
#define printd(...)
#endif

static inline void enable() {
	#ifdef _TWO_MOSFET_
	GPIO_SetBits(EN_GPIOx, EN_GPIO_Pin_n);
	#else
	GPIO_ResetBits(EN_GPIOx, EN_GPIO_Pin_n);
	#endif
}
static inline void disable() {
	#ifdef _TWO_MOSFET_
	GPIO_ResetBits(EN_GPIOx, EN_GPIO_Pin_n);
	#else
	GPIO_SetBits(EN_GPIOx, EN_GPIO_Pin_n);
	#endif
}

int8 last_battery_life = 0;

static inline int8 readAdc() {
	uint16 adcv;
	enable();
	//刚刚启动端口后需要电压建立，因此第一个电压值不准确，需要丢弃。
	adcv = Adc_getValue(ADC_CHANNEL);	
	adcv = Adc_getValue(ADC_CHANNEL);
	disable();
	float battery_life;
	float voltage;
	voltage = Adc_getVoltage(adcv);
	battery_life = (voltage*2 - 3.6) / (4.2-3.6) * 100;
	if(battery_life > 125){//电池可能工作在异常状态，如果过压充电，电池电量可能超过100%
		battery_life = 125;
	}
	if(battery_life < -125){//电池如果欠压，损坏，或者不存在，电量百分比可能低于0，
		battery_life = -125;
	}
	//但必须保证电量百分比在int8范围内
	last_battery_life = (int8)battery_life;
	return (int8)battery_life;

}

static uint16 report_addr = 0;
static uint8 report_port = 32;
static int8 report_low_value = 20;
static uint32 report_interval = 10*1000;
static int32 last_report_time = 1000 - 10 * 1000;

void Battery_msTick(void) {	
	if (main_Clk >= last_report_time + report_interval) {
		last_report_time = main_Clk;
		int8 adc = readAdc();
		printd("battery life %d %%\n", adc);
		if(adc < report_low_value){
			sCc9dPkg send_pkg;
			send_pkg.from_port = LOCAL_PORT;
			send_pkg.to_port = report_port;
			send_pkg.remote_addr = report_addr;
			send_pkg.datas[0] = 'L';
			send_pkg.datas[1] = adc;
			cc9d_SendPkg(&send_pkg, 2);
		}
	}
}


void Battery_init() {
	GPIO_InitTypeDef  GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
	
	//首先初始化电池电流采集开关，因为可能会使用低功耗，而采集电池电量需要分压电阻，所以需要在采集时打开，采集完毕后关闭。
	GPIO_InitStructure.GPIO_Pin = EN_GPIO_Pin_n;
	#ifdef _TWO_MOSFET_
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	#else
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	#endif
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(EN_GPIOx, &GPIO_InitStructure);
	disable();

	//初始化ADC端口
	GPIO_InitStructure.GPIO_Pin = ADC_GPIO_Pin_n;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(ADC_GPIOx, &GPIO_InitStructure);
}



//电池命令包括：
// 	r: 读取电池电量，
// 	a: 设置电量过低报警地址和端口
// 	b: 设置电量过低报警值
void Battery_recvPkg(sCc9dPkg* recv_pkg, uint8 data_len){
	if (data_len == 0) {
		return;
	}
	switch (recv_pkg->datas[0]) {
	case 'r': {//0007.12:/r
		sCc9dPkg send_pkg;
		createResponsePkg(&send_pkg, recv_pkg);
		send_pkg.datas[0] = 'R';
		send_pkg.datas[1] = last_battery_life;
		cc9d_SendPkg(&send_pkg, 2);
		printd("read battery life, get  %d %%\n", last_battery_life);
	}break;
	//设置报告低电量的地址和端口
	case 'a': {//0007.12:/a w0 |32
		if (data_len != 4) {
			responePkg(recv_pkg, BAD_LENGTH);
			return;
		}
		#pragma pack(1)//这里的结构体考虑了命令的第一字节。		
		struct {
			uint8 cmd;
			uint16 node_addr;
			uint8 port;
		}*arguments = (void*)(recv_pkg->datas);
		#pragma pack()
		report_addr = arguments->node_addr;
		report_port = arguments->port;

		sCc9dPkg send_pkg;
		createResponsePkg(&send_pkg, recv_pkg);
		send_pkg.datas[0] = 'A';
		cc9d_SendPkg(&send_pkg, 1);
		//打印设置记录日志
		printd("baterry report to %4x.%d\n", report_addr, report_port);
	}break;
	//设置报告低电量的阈值百分数，和监测时间长度（秒）
	case 'b': {//0007.12:/b |20 w5 
		if (data_len != 4) {
			responePkg(recv_pkg, BAD_LENGTH);
			return;
		}
	#pragma pack(1)//这里的结构体考虑了命令的第一字节。		
		struct {
			uint8 cmd;
			int8 low_value;
			uint16 delay_second;
		}*arguments = (void*)(recv_pkg->datas);
	#pragma pack()
		report_low_value = arguments->low_value;
		report_interval = arguments->delay_second*1000;
		last_report_time = main_Clk;

		sCc9dPkg send_pkg;
		createResponsePkg(&send_pkg, recv_pkg);
		send_pkg.datas[0] = 'B';
		cc9d_SendPkg(&send_pkg, 1);
		printd("battery report low value %d, interval %d s.\n", report_low_value, arguments->delay_second);
	}break;
	default:
	break;
	}
}

